package semant.second_pass;

import util.List;
import semant.Env;
import symbol.Symbol;
import symbol.ClassInfo;
import symbol.MethodInfo;
import symbol.VarInfo;
import syntaxtree.Statement;
import syntaxtree.ArrayAssign;
import syntaxtree.Assign;
import syntaxtree.Block;
import syntaxtree.Exp;
import syntaxtree.Identifier;
import syntaxtree.If;
import syntaxtree.Print;
import syntaxtree.While;
import syntaxtree.Type;
import syntaxtree.BooleanType;
import syntaxtree.IntegerType;
import syntaxtree.IntArrayType;
import syntaxtree.IdentifierType;
import syntaxtree.VisitorAdapter;

final class StatementHandler extends VisitorAdapter {

    private Env env;
    private ClassInfo cinfo;
    private MethodInfo minfo;

    private StatementHandler(Env e, ClassInfo c, MethodInfo m){
	super();
	env = e;
	cinfo = c;
	minfo = m;
    }

    static void secondPass(Env e, ClassInfo c, MethodInfo m, Statement s){
	StatementHandler h = new StatementHandler(e, c, m);
	s.accept(h);
    }

    public void visit(ArrayAssign node){
	Symbol name = Symbol.symbol(node.var.s);
	VarInfo v = ExpHandler.getVariable(cinfo, minfo, name);
	// verifica se a variavel representa um vetor
	if(v == null){
	    env.err.Error(node, new Object[]{"Variavel '" + name + "' nao declarada no metodo atual"});
	}else if(!(v.type instanceof IntArrayType)){
	    // nao eh um vetor!
	    env.err.Error(node,
			  new Object[]{"Tipo invalido de objeto para consulta de elemento de vetor",
				       "Esperado: int []",
				       "Encontrado: " + v.type});
	}

	// verifica indice do vetor
	Type index = ExpHandler.secondPass(env, cinfo, minfo, node.index);
	if(!(index instanceof IntegerType))
	    env.err.Error(node.index,
			  new Object[]{"Tipo invalido de objeto para indice de vetor",
				       "Esperado: int",
				       "Encontrado: " + index});

	// verifica valor atribuido
	Type value = ExpHandler.secondPass(env, cinfo, minfo, node.value);
	if(!(value instanceof IntegerType))
	    env.err.Error(node.value,
			  new Object[]{"Tipo invalido de objeto para atribuicao a elemento de um vetor",
				       "Esperado: int",
				       "Encontrado: " + value});

    }

    public void visit(Assign node){
	Symbol name = Symbol.symbol(node.var.s);
	VarInfo v = ExpHandler.getVariable(cinfo, minfo, name);
	// verifica se a variavel foi declarada
	if(v == null)
	    env.err.Error(node, new Object[]{"Variavel '" + name + "' nao declarada no metodo atual"});

	// verifica tipos do lado esquerdo e direito
	Type exp = ExpHandler.secondPass(env, cinfo, minfo, node.exp);
	if(v != null && !TypeHandler.compatible(env, v.type, exp))
	    env.err.Error(node, new Object[]{"Expressao da atribuicao incompativel com o tipo da variavel",
					     "Esperado: " + v.type,
					     "Encontrado: " + exp});
    }

    public void visit(Block node){
	for(List<Statement> s = node.body; s != null; s = s.tail)
	    StatementHandler.secondPass(env, cinfo, minfo, s.head);
    }

    public void visit(If node){
	// verifica condicao do if
	Type condition = ExpHandler.secondPass(env, cinfo, minfo, node.condition);
	if(!(condition instanceof BooleanType))
	    env.err.Error(node.condition,
			  new Object[]{"Tipo invalido para condicao do 'if'",
				       "Esperado: boolean",
				       "Encontrado: " + condition});

	// verifica clausula then
	if(node.thenClause != null)
	    StatementHandler.secondPass(env, cinfo, minfo, node.thenClause);
	// verifica clausula else
	if(node.elseClause != null)
	    StatementHandler.secondPass(env, cinfo, minfo, node.elseClause);
    }

    public void visit(Print node){
	// imprime apenas ints!
	Type exp = ExpHandler.secondPass(env, cinfo, minfo, node.exp);
	if(!(exp instanceof IntegerType))
	    env.err.Error(node.exp,
			  new Object[]{"Tipo invalido de objeto para impressao",
				       "Esperado: int",
				       "Encontrado: " + exp});
    }

    public void visit(While node){
	// verifica condicao do while
	Type condition = ExpHandler.secondPass(env, cinfo, minfo, node.condition);
	if(!(condition instanceof BooleanType))
	    env.err.Error(node.condition,
			  new Object[]{"Tipo invalido para condicao do 'while'",
				       "Esperado: boolean",
				       "Encontrado: " + condition});

	// verifica corpo do while
	StatementHandler.secondPass(env, cinfo, minfo, node.body);
    }

}
